<!DOCTYPE html><html><head hexo-theme="https://volantis.js.org/#3.0-beta1"><meta charset="utf-8"><meta name="renderer" content="webkit"><meta name="force-rendering" content="webkit"><meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1"><meta name="HandheldFriendly" content="True"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1"><title>ReactiveCocoa 响应式编程初探 - xaoxuu</title><meta name="keywords" content="iOS,ReactiveCocoa,响应式编程"><meta name="description" content="RAC 即 ReactiveCocoa，是 Github 开源的函数响应式编程框架。可以简化代码，使一个功能的代码集中到一起，提高了代码聚合性。"><link rel="alternate" href="/atom.xml" title="xaoxuu"><meta name="theme-color" content="#FFFFFF"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.13/css/all.min.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/node-waves@0.7.6/dist/waves.min.css"><link rel="shortcut icon" type="image/x-icon" sizes="16x16 32x32 48x48 64x64" href="/assets/favicon/favicon.ico"><link rel="apple-touch-icon" type="image/png" sizes="180x180" href="/assets/favicon/favicon_180.png"><link rel="mask-icon" color="#1BC3FB" href="/assets/favicon/favicon_180.png"><link rel="manifest" href="/manifest.json"><link rel="stylesheet" href="/css/style.css"></head><body><div class="cover-wrapper"><cover class="cover post" style="display:none"><div class="cover-body"><div class="a"><p class="title">XAOXUU</p><p class="subtitle">「山重水复疑无路 柳暗花明又一村」</p></div><div class="b"><div class="m_search"><form name="searchform" class="form u-search-form"><input type="text" class="input u-search-input" placeholder=""> <i class="icon fas fa-search fa-fw"></i></form></div><div class="menu navigation"><ul class="cover-list-h"><li><a class="nav home" href="/" id="home"><i class="fas fa-rss fa-fw"></i>博文</a></li><li><a class="nav home" href="/blog/archives/" rel="nofollow" id="blogarchives"><i class="fas fa-archive fa-fw"></i>归档</a></li><li><a class="nav home" href="/wiki/" id="wiki"><i class="fas fa-book fa-fw"></i>文档</a></li><li><a class="nav home" href="/friends/" rel="nofollow" target="_self" id="friends"><i class="fas fa-link fa-fw"></i>友链</a></li><li><a class="nav home" href="/about/" rel="nofollow" id="about"><i class="fas fa-info-circle fa-fw"></i>关于</a></li></ul></div></div></div><div class="scroll-down" style="display:none"><i class="fa fa-chevron-down scroll-down-effects"></i></div></cover><header class="l_header shadow blur"><div class="container"><div class="wrapper"><div class="nav-sub"><p class="title"></p><ul class="switcher nav-list-h"><li><a class="s-comment fas fa-comments fa-fw" target="_self" href="javascript:void(0)"></a></li><li><a class="s-toc fas fa-list fa-fw" target="_self" href="javascript:void(0)"></a></li></ul></div><div class="nav-main"><a class="title flat-box" target="_self" href="/">XAOXUU</a><div class="menu navigation"><ul class="nav-list-h"><li><a class="flat-box"><i class="fas fa-rss fa-fw"></i>博文</a><ul class="list-v"><li class="header"><i class="fas fa-fire fa-fw"></i>热门文章</li><li><a class="flat-box" href="/blog/2019-08-27-prohud/" id="blog2019-08-27-prohud">如何设计一个样式和逻辑分离的组件</a></li><li><a class="flat-box" href="/blog/2019-08-29-valuex/" id="blog2019-08-29-valuex">使用链式语法打造一个对象类型转换库</a></li><li><a class="flat-box" href="/blog/2019-07-23-heartmate/" id="blog2019-07-23-heartmate">心率管家 App 的设计与开发流程</a></li><hr><li><a class="flat-box" href="/blog/archives/" id="blogarchives">查看所有文章 <i class="fas fa-chevron-right fa-fw"></i></a></li></ul></li><li><a class="flat-box"><i class="fas fa-cube fa-fw"></i>项目</a><ul class="list-v"><li class="header"><i class="fas fa-fire fa-fw"></i>热门 iOS 开源库</li><li><a class="flat-box" href="/wiki/prohud/" id="wikiprohud">ProHUD：功能强大的 HUD 库</a></li><li><a class="flat-box" href="/wiki/valuex/" id="wikivaluex">ValueX：ObjC 对象类型转换库</a></li><li><a class="flat-box" href="/wiki/inspire/" id="wikiinspire">Inspire：Swift 主题库</a></li><hr><li class="header"><i class="fas fa-book fa-fw"></i>知识体系归纳</li><li><a class="flat-box" href="/wiki/ios/" id="wikiios">Apple Developer</a></li><li><a class="flat-box" href="/wiki/git/" id="wikigit">Git 实用教程</a></li><hr><li><a class="flat-box" href="/wiki/" id="wiki">查看所有项目 <i class="fas fa-chevron-right fa-fw"></i></a></li></ul></li><li><a class="flat-box"><i class="fas fa-ellipsis-v fa-fw"></i>更多</a><ul class="list-v"><li><a class="flat-box" href="/about/" id="about"><i class="fas fa-user-circle fa-fw"></i>关于</a></li><hr><li><a class="flat-box" href="/notes/" id="notes"><i class="fas fa-pen-square fa-fw"></i>便笺</a></li><li><a class="flat-box" href="/bookmark/" id="bookmark"><i class="fas fa-bookmark fa-fw"></i>收藏夹</a></li><li><a class="flat-box"><i class="fas fa-share fa-fw"></i>分享</a><ul class="list-v"><li><a class="flat-box" href="/share/music/" id="sharemusic"><i class="fas fa-music fa-fw"></i>网易云音乐歌单</a></li></ul></li><li><a class="flat-box" href="/friends/" id="friends"><i class="fas fa-link fa-fw"></i>友链</a></li><hr><li><a class="flat-box" href="https://github.com/xaoxuu" id="https:githubcomxaoxuu" rel="external nofollow noopener noreferrer" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a></li></ul></li></ul></div><div class="m_search"><form name="searchform" class="form u-search-form"><i class="icon fas fa-search fa-fw"></i> <input type="text" class="input u-search-input" placeholder="Search..."></form></div><ul class="switcher nav-list-h"><li><a class="s-search fas fa-search fa-fw" target="_self" href="javascript:void(0)"></a></li><li><a class="s-menu fas fa-bars fa-fw" target="_self" href="javascript:void(0)"></a><ul class="menu-phone list-v navigation white-box"><li><a class="flat-box"><i class="fas fa-rss fa-fw"></i>博文</a><ul class="list-v"><li class="header"><i class="fas fa-fire fa-fw"></i>热门文章</li><li><a class="flat-box" href="/blog/2019-08-27-prohud/" id="blog2019-08-27-prohud">如何设计一个样式和逻辑分离的组件</a></li><li><a class="flat-box" href="/blog/2019-08-29-valuex/" id="blog2019-08-29-valuex">使用链式语法打造一个对象类型转换库</a></li><li><a class="flat-box" href="/blog/2019-07-23-heartmate/" id="blog2019-07-23-heartmate">心率管家 App 的设计与开发流程</a></li><hr><li><a class="flat-box" href="/blog/archives/" id="blogarchives">查看所有文章 <i class="fas fa-chevron-right fa-fw"></i></a></li></ul></li><li><a class="flat-box"><i class="fas fa-cube fa-fw"></i>项目</a><ul class="list-v"><li class="header"><i class="fas fa-fire fa-fw"></i>热门 iOS 开源库</li><li><a class="flat-box" href="/wiki/prohud/" id="wikiprohud">ProHUD：功能强大的 HUD 库</a></li><li><a class="flat-box" href="/wiki/valuex/" id="wikivaluex">ValueX：ObjC 对象类型转换库</a></li><li><a class="flat-box" href="/wiki/inspire/" id="wikiinspire">Inspire：Swift 主题库</a></li><hr><li class="header"><i class="fas fa-book fa-fw"></i>知识体系归纳</li><li><a class="flat-box" href="/wiki/ios/" id="wikiios">Apple Developer</a></li><li><a class="flat-box" href="/wiki/git/" id="wikigit">Git 实用教程</a></li><hr><li><a class="flat-box" href="/wiki/" id="wiki">查看所有项目 <i class="fas fa-chevron-right fa-fw"></i></a></li></ul></li><li><a class="flat-box"><i class="fas fa-ellipsis-v fa-fw"></i>更多</a><ul class="list-v"><li><a class="flat-box" href="/about/" id="about"><i class="fas fa-user-circle fa-fw"></i>关于</a></li><hr><li><a class="flat-box" href="/notes/" id="notes"><i class="fas fa-pen-square fa-fw"></i>便笺</a></li><li><a class="flat-box" href="/bookmark/" id="bookmark"><i class="fas fa-bookmark fa-fw"></i>收藏夹</a></li><li><a class="flat-box"><i class="fas fa-share fa-fw"></i>分享</a><ul class="list-v"><li><a class="flat-box" href="/share/music/" id="sharemusic"><i class="fas fa-music fa-fw"></i>网易云音乐歌单</a></li></ul></li><li><a class="flat-box" href="/friends/" id="friends"><i class="fas fa-link fa-fw"></i>友链</a></li><hr><li><a class="flat-box" href="https://github.com/xaoxuu" id="https:githubcomxaoxuu" rel="external nofollow noopener noreferrer" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a></li></ul></li></ul></li></ul></div></div></div></header></div><div class="l_body nocover"><div class="body-wrapper" id="pjax-container"><div id="pjax-data" style="display:none"><div id="pjax-ispage">true</div><div id="pjax-pageTitle">ReactiveCocoa 响应式编程初探</div><div id="pjax-enable-cover">true</div><div id="valine-path">none</div><div id="valine-placeholder">none</div></div><script>document.getElementsByClassName("cover")[0].style.display="none",document.getElementsByClassName("l_body")[0].classList.add("nocover"),document.getElementsByClassName("l_header","cover-wrapper")[0].classList.add("show")</script><div class="l_main"><article id="post" class="post white-box reveal shadow article-type-post" itemscope="" itemprop="blogPost"><section class="meta"><div class="meta" id="header-meta"><h1 class="title"><a href="/blog/2016-07-20-reactive-cocoa/">ReactiveCocoa 响应式编程初探</a></h1><div class="new-meta-box"><div class="new-meta-item date"><a class="notlink"><i class="fas fa-calendar-alt fa-fw" aria-hidden="true"></i><p>2016-07-20</p></a></div><div class="new-meta-item browse valine"><a class="notlink"><i class="fas fa-eye fa-fw" aria-hidden="true"></i> <span id="/blog/2016-07-20-reactive-cocoa/" class="leancloud_visitors" data-flag-title="ReactiveCocoa 响应式编程初探"><p><span class="leancloud-visitors-count"></span></p></span></a></div></div><hr></div></section><section class="article typo"><div class="article-entry" itemprop="articleBody"><p>RAC 即 ReactiveCocoa，是 Github 开源的函数响应式编程框架。可以简化代码，使一个功能的代码集中到一起，提高了代码聚合性。</p><a id="more"></a><ol><li>创建信号（冷信号）</li><li>订阅信号（热信号）</li><li>发送信号</li></ol><figure class="highlight objc"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 1. 创建信号，点进去可以看到：</span></span><br><span class="line"><span class="comment">// 它其实创建了一个RACDynamicSignal信号，并保存了didSubscribe这个Block</span></span><br><span class="line">RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt; _Nonnull subscriber) &#123;</span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"创建了信号"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 3. 发送信号，点进去可以看到：</span></span><br><span class="line">    <span class="comment">// 执行之前所保存的nextBlock</span></span><br><span class="line">    [subscriber sendNext:<span class="string">@"ZM"</span>];</span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"发送了信号"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> [RACDisposable disposableWithBlock:^&#123;</span><br><span class="line">        <span class="comment">// 取消订阅时的回调</span></span><br><span class="line">    &#125;];</span><br><span class="line">&#125;];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 订阅信号，点进去可以看到：</span></span><br><span class="line"><span class="comment">// 创建了一个RACSubscriber订阅者，并保存了一个nextBlock，</span></span><br><span class="line"><span class="comment">// 同时执行之前所保存的didSubscribe这个Block</span></span><br><span class="line">[signal subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"订阅了信号"</span>);</span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line"></span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><h2 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h2><ul><li>代替代理（更少代码）<code>rac_signalForSelector:</code></li><li>代替 KVO <code>rac_valuesAndChangesForKeyPath:</code></li><li>监听事件（用于 MVVM）<code>rac_signalForControllerEvents:</code></li><li>代替通知（自动 remove）<code>rac_addObserverForName:</code></li><li>监听文本框内容改变 <code>rac_textSignal:</code></li><li>处理当界面有多次请求时，需要都获取到数据时，才能展示界面 <code>rac_liftSelector:WithSignalFromArray:Signals:</code></li></ul><h2 id="常用方法"><a href="#常用方法" class="headerlink" title="常用方法"></a>常用方法</h2><h3 id="map-amp-flattenMap"><a href="#map-amp-flattenMap" class="headerlink" title="map &amp;  flattenMap"></a>map &amp; flattenMap</h3><p>这是两个映射方法，将源信号内容映射成新的内容，就像 signal 管道上的中间处理器，从这里走过的 signal 都会经过一段处理后，变成新的 signal 继续传输。</p><figure class="highlight objc"><figcaption><span>map</span></figcaption><table><tr><td class="code"><pre><span class="line"><span class="comment">// map，将输出NSNumber的signal转换成输出NSString</span></span><br><span class="line">RACSignal *mapSignal = [[RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt; _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">1</span>)];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;] map:^<span class="keyword">id</span> _Nullable(<span class="keyword">id</span>  _Nullable value) &#123;</span><br><span class="line">    <span class="keyword">return</span> [<span class="built_in">NSString</span> stringWithFormat:<span class="string">@"%@"</span>, value];</span><br><span class="line">&#125;];</span><br><span class="line">[mapSignal subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// NSString类型</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"x"</span>);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><figure class="highlight objc"><figcaption><span>flattenMap</span></figcaption><table><tr><td class="code"><pre><span class="line"><span class="comment">// flattenMap，将输出NSNumber的signal转换成输出NSString</span></span><br><span class="line">RACSignal *mapSignal = [[RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">1</span>)];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;] flattenMap:^__kindof RACSignal * _Nullable(<span class="keyword">id</span>  _Nullable value) &#123;</span><br><span class="line">    <span class="keyword">return</span> [RACReturnSignal <span class="keyword">return</span>:[<span class="built_in">NSString</span> stringWithFormat:<span class="string">@"%@"</span>, value]];</span><br><span class="line">&#125;];</span><br><span class="line">[mapSignal subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// NSString类型</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"x"</span>);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><p>两者之间的区别就在于，map 中 Block 返回转换对象，flattenMap 返回转换对象的信号。一般信号发出的值不是信号，使用 map；如果是信号则使用 flattenMap，它可以处理信号中的信号。</p><h3 id="merge"><a href="#merge" class="headerlink" title="merge"></a>merge</h3><p>把多个信号合并成一个信号，只需订阅这一个信号就相当于订阅了多个信号，任何一个信号有新值的时候都会触发调用。</p><figure class="highlight objc"><figcaption><span>merge</span></figcaption><table><tr><td class="code"><pre><span class="line">RACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">1</span>)];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;];</span><br><span class="line">RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">2</span>)];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;];</span><br><span class="line">RACSignal *signal3 = [RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">3</span>)];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;];</span><br><span class="line">RACSignal *mergeSignal = [RACSignal merge:@[signal1, signal2, signal3]];</span><br><span class="line">[mergeSignal subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 分别输出1,2,3</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><h3 id="concat"><a href="#concat" class="headerlink" title="concat"></a>concat</h3><p>将多个信号有顺序的连接起来，按照顺序接收信号，但是一定要之前的信号完成了才能发送下一个信号。</p><figure class="highlight objc"><figcaption><span>concat</span></figcaption><table><tr><td class="code"><pre><span class="line">RACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">1</span>)];</span><br><span class="line">    <span class="comment">// 发送信号完成，表示不再订阅了，内部会自动调用[RACDisposable disposable]取消订阅信号。</span></span><br><span class="line">    [subscriber sendCompleted];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;];</span><br><span class="line">RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">2</span>)];</span><br><span class="line">    [subscriber sendCompleted];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;];</span><br><span class="line">RACSignal *signal3 = [RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">3</span>)];</span><br><span class="line">    [subscriber sendCompleted];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;];</span><br><span class="line">RACSignal *concatSignal = [RACSignal concat:@[signal2, signal1, signal3]];</span><br><span class="line">[concatSignal subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 分别输出2,1,3</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><h3 id="then"><a href="#then" class="headerlink" title="then"></a>then</h3><p>用于连接两个信号，内部也是使用 concat，当前一个信号完成之后才会连接 then 返回的信号，但是会忽略前一个信号，只会触发下个信号。</p><figure class="highlight objc"><figcaption><span>then</span></figcaption><table><tr><td class="code"><pre><span class="line">RACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">1</span>)];</span><br><span class="line">    [subscriber sendCompleted];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;];</span><br><span class="line">RACSignal *thenSignal =  [signal1 then:^RACSignal * _Nonnull&#123;</span><br><span class="line">  <span class="keyword">return</span>  [RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">       [subscriber sendNext:@(<span class="number">2</span>)];</span><br><span class="line">       [subscriber sendCompleted];</span><br><span class="line">       <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">   &#125;];</span><br><span class="line">&#125;];</span><br><span class="line">[thenSignal subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 输出2</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><h3 id="zip"><a href="#zip" class="headerlink" title="zip"></a>zip</h3><p>将对各信号压缩成一个信号，只有当几个信号同时 sendNext 的时候才会触发压缩流的 next 事件，其中每一个信号 send 的内容都是一一对应的。</p><figure class="highlight objc"><figcaption><span>zip</span></figcaption><table><tr><td class="code"><pre><span class="line">RACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">1</span>)];</span><br><span class="line">    <span class="comment">// 不会覆盖上一个信号</span></span><br><span class="line">    [subscriber sendNext:@(<span class="number">3</span>)];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;];</span><br><span class="line">RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">2</span>)];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;] ;</span><br><span class="line">RACSignal *zipSignal = [RACSignal zip:@[signal1, signal2]];</span><br><span class="line">[zipSignal subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 输出(1, 2)</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><h3 id="combineLatest"><a href="#combineLatest" class="headerlink" title="combineLatest"></a>combineLatest</h3><p>将多个信号组合起来，当其中每一个信号都 sendNext 之后，才会触发组合的信号，其中每一个信号再次 sendNext 都会覆盖之前的信号内容，返回的是一个 RACTuple（元组，类似于NSArray）。</p><figure class="highlight objc"><figcaption><span>combineLatest</span></figcaption><table><tr><td class="code"><pre><span class="line">RACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">1</span>)];</span><br><span class="line">    <span class="comment">// 将覆盖之前的信号，这就是跟zip的区别</span></span><br><span class="line">    [subscriber sendNext:@(<span class="number">3</span>)];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;];</span><br><span class="line">RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">2</span>)];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;];</span><br><span class="line"><span class="comment">//如果其中一个信号不sendNext，则不会触发组合信号</span></span><br><span class="line">RACSignal *combineSignal = [RACSignal combineLatest:@[signal1, signal2]];</span><br><span class="line">[combineSignal subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 输出(3, 2)</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><h3 id="rac-liftSelector-withSignalsFromArray"><a href="#rac-liftSelector-withSignalsFromArray" class="headerlink" title="rac_liftSelector: withSignalsFromArray:"></a>rac_liftSelector: withSignalsFromArray:</h3><p>当信号组中每一个信号都至少一次 sendNext 之后，将触发 Selector 方法，类似于 combineLatest。</p><figure class="highlight objc"><figcaption><span>rac_liftSelector: withSignalsFromArray:</span></figcaption><table><tr><td class="code"><pre><span class="line">RACSubject *subject1 = [RACSubject subject];</span><br><span class="line">RACSubject *subject2 = [RACSubject subject];</span><br><span class="line">[[<span class="keyword">self</span> rac_liftSelector:<span class="keyword">@selector</span>(updateWithParameter1:parameter2:) withSignals:subject1, subject2, <span class="literal">nil</span>] subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br><span class="line">[subject1 sendNext:@<span class="number">1</span>];</span><br><span class="line">[subject2 sendNext:@<span class="number">2</span>];</span><br></pre></td></tr></table></figure><h3 id="reduceEach"><a href="#reduceEach" class="headerlink" title="reduceEach"></a>reduceEach</h3><p>一般用于元组，把元组的值聚合成一个值。</p><figure class="highlight objc"><figcaption><span>reduceEach</span></figcaption><table><tr><td class="code"><pre><span class="line">RACSignal *combineSignal = [RACSignal combineLatest:@[signal1, signal2]];</span><br><span class="line">RACSignal *reduceSignal = [combineSignal reduceEach:^<span class="keyword">id</span> (<span class="built_in">NSNumber</span> *num1, <span class="built_in">NSNumber</span> *num2)&#123;</span><br><span class="line">    <span class="keyword">return</span> @(num1.doubleValue+num2.doubleValue);</span><br><span class="line">&#125;];</span><br><span class="line">[reduceSignal subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 输出3</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br><span class="line"><span class="comment">// 等同于：`+ (RACSignal *)combineLatest:(id&lt;NSFastEnumeration&gt;)signals reduce:(id (^)())reduceBlock`</span></span><br><span class="line"><span class="comment">// RACSignal *combineSignal = [RACSignal combineLatest:@[signal1,signal2] reduce:^id (NSNumber *num1, NSNumber *num2)&#123;</span></span><br><span class="line"><span class="comment">//     return @(num1.doubleValue+num2.doubleValue);</span></span><br><span class="line"><span class="comment">// &#125;];</span></span><br></pre></td></tr></table></figure><h3 id="filter"><a href="#filter" class="headerlink" title="filter"></a>filter</h3><p>过滤信号，添加筛选条件，只有符合的才会触发调用。</p><figure class="highlight objc"><figcaption><span>filter</span></figcaption><table><tr><td class="code"><pre><span class="line">RACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">1</span>)];</span><br><span class="line">    [subscriber sendNext:<span class="string">@"3"</span>];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;];</span><br><span class="line">RACSignal *filterSignal = [signal1 filter:^<span class="built_in">BOOL</span>(<span class="keyword">id</span>  _Nullable value) &#123;</span><br><span class="line">    <span class="keyword">return</span> [value isKindOfClass:[<span class="built_in">NSNumber</span> <span class="keyword">class</span>]];</span><br><span class="line">&#125;];</span><br><span class="line">[filterSignal subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 输出1</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 数组的筛选</span></span><br><span class="line">RACSequence *sequence = [@[@(<span class="number">1</span>),@(<span class="number">2</span>),<span class="string">@"3"</span>].rac_sequence filter:^<span class="built_in">BOOL</span>(<span class="keyword">id</span>  _Nullable value) &#123;</span><br><span class="line">    <span class="keyword">return</span> [value isKindOfClass:[<span class="built_in">NSNumber</span> <span class="keyword">class</span>]];</span><br><span class="line">&#125;];</span><br><span class="line">[sequence.signal subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 输出1, 2</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><h3 id="distinctUntilChanged"><a href="#distinctUntilChanged" class="headerlink" title="distinctUntilChanged"></a>distinctUntilChanged</h3><p>当前值跟上一次的值不同的时候，就会触发调用，否则被忽略。</p><figure class="highlight objc"><figcaption><span>distinctUntilChanged</span></figcaption><table><tr><td class="code"><pre><span class="line">[textField.rac_textSignal.distinctUntilChanged subscribeNext:^(<span class="built_in">NSString</span> * _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 变化时输出变化之后的值</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><h3 id="take"><a href="#take" class="headerlink" title="take"></a>take</h3><p>从第一个信号开始设置信号发送的有效的个数。</p><figure class="highlight objc"><figcaption><span>take</span></figcaption><table><tr><td class="code"><pre><span class="line">RACSignal *signal = [[RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">1</span>)];</span><br><span class="line">    [subscriber sendNext:@(<span class="number">2</span>)];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;] take:<span class="number">1</span>];</span><br><span class="line"></span><br><span class="line">[signal subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 输出1，因为take为1，所以有效的只有最开始的那一个，其他的忽略掉了</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><h3 id="takeLast"><a href="#takeLast" class="headerlink" title="takeLast"></a>takeLast</h3><p>从最后一个开始设置信号发送的有效个数，必须 sendCompleted，不然不知道总共多少个信号。</p><figure class="highlight objc"><figcaption><span>takeLast</span></figcaption><table><tr><td class="code"><pre><span class="line">[[[RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">1</span>)];</span><br><span class="line">    [subscriber sendNext:@(<span class="number">2</span>)];</span><br><span class="line">    [subscriber sendCompleted];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;] takeLast:<span class="number">1</span>] subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 输出2</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><h3 id="takeUntil"><a href="#takeUntil" class="headerlink" title="takeUntil"></a>takeUntil</h3><p><code>[signal1 takeUntil:signal2]</code>，当 signal2 已经 sendNext 或者 sendCompleted，signal1 就会失效。</p><figure class="highlight objc"><figcaption><span>takeUntil</span></figcaption><table><tr><td class="code"><pre><span class="line">RACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">1</span>)];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;];</span><br><span class="line">RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">2</span>)];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;];</span><br><span class="line">[[signal1 takeUntil:signal2] subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 什么都不会输出，因为signal2已经sendNext，所以signal1就会失效</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><h3 id="skip"><a href="#skip" class="headerlink" title="skip"></a>skip</h3><p>跳跃，从第一个发出的信号开始跳。</p><figure class="highlight objc"><figcaption><span>skip</span></figcaption><table><tr><td class="code"><pre><span class="line">[[[RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">1</span>)];</span><br><span class="line">    [subscriber sendNext:@(<span class="number">2</span>)];</span><br><span class="line">    [subscriber sendNext:@(<span class="number">3</span>)];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;] skip:<span class="number">2</span>] subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 输出3</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><h3 id="doNext"><a href="#doNext" class="headerlink" title="doNext"></a>doNext</h3><p>在执行 sendNext 之前会执行这个。</p><figure class="highlight objc"><figcaption><span>doNext</span></figcaption><table><tr><td class="code"><pre><span class="line">[[[RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">1</span>)];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;] doNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    x = [<span class="built_in">NSString</span> stringWithFormat:<span class="string">@"%@haha"</span>,x];</span><br><span class="line">    <span class="comment">// 输出1haha，在订阅回调之前执行</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;] subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 输出1</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><h3 id="timeout"><a href="#timeout" class="headerlink" title="timeout"></a>timeout</h3><p>在超过设定时间范围之后让信号报错，且不能发送内容。</p><figure class="highlight objc"><figcaption><span>timeout</span></figcaption><table><tr><td class="code"><pre><span class="line">RACSubject *subject = [RACSubject subject];</span><br><span class="line">[[subject timeout:<span class="number">3</span> onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 只输出1</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125; error:^(<span class="built_in">NSError</span> * _Nullable error) &#123;</span><br><span class="line">    <span class="comment">// 3秒之后输出错误日志</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, error);</span><br><span class="line">&#125;];</span><br><span class="line">[subject sendNext:@<span class="number">1</span>];</span><br><span class="line">dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<span class="number">4</span> * <span class="built_in">NSEC_PER_SEC</span>)), dispatch_get_main_queue(), ^&#123;</span><br><span class="line">    [subject sendNext:@<span class="number">2</span>];</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="interval"><a href="#interval" class="headerlink" title="interval"></a>interval</h3><p>定时，每隔一定时间发出时间信号。</p><figure class="highlight objc"><figcaption><span>interval</span></figcaption><table><tr><td class="code"><pre><span class="line"><span class="comment">// RACScheduler:队列</span></span><br><span class="line">[[RACSignal interval:<span class="number">1</span> onScheduler:[RACScheduler currentScheduler]] subscribeNext:^(<span class="built_in">NSDate</span> * _Nullable x) &#123;</span><br><span class="line">        <span class="comment">// 每隔一秒输出当前时间</span></span><br><span class="line">        <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><h3 id="delay"><a href="#delay" class="headerlink" title="delay"></a>delay</h3><p>延时发送信号。</p><figure class="highlight objc"><figcaption><span>delay</span></figcaption><table><tr><td class="code"><pre><span class="line">[[[RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(<span class="number">1</span>)];</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;] delay:<span class="number">3</span>] subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 3秒之后输出1</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><h3 id="retry"><a href="#retry" class="headerlink" title="retry"></a>retry</h3><p>重试，只要失败，就会重新执行创建信号中的 block，直到成功。</p><figure class="highlight objc"><figcaption><span>retry</span></figcaption><table><tr><td class="code"><pre><span class="line">__block <span class="built_in">NSInteger</span> i = <span class="number">0</span>;</span><br><span class="line">[[[RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    i++;</span><br><span class="line">    <span class="keyword">if</span> (i &gt; <span class="number">10</span>) &#123;</span><br><span class="line">        [subscriber sendNext:@(<span class="number">1</span>)];</span><br><span class="line">    &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">        [subscriber sendError:<span class="literal">nil</span>];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;] retry] subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 重试10次之后输出1</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125; error:^(<span class="built_in">NSError</span> * _Nullable error) &#123;</span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, error);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><h3 id="throttle"><a href="#throttle" class="headerlink" title="throttle"></a>throttle</h3><p>节流，当某个信号发送比较频繁的时候，可以限制在一定之间内不接受信号，等过了这个时间再取最后发送的信号内容发出，类似于 <code>bufferWithTime:onScheduler:</code>。</p><figure class="highlight objc"><figcaption><span>throttle</span></figcaption><table><tr><td class="code"><pre><span class="line">RACSubject *subject = [RACSubject subject];</span><br><span class="line"><span class="comment">// [subject bufferWithTime:1 onScheduler:[RACScheduler currentScheduler]];</span></span><br><span class="line">[[subject throttle:<span class="number">1</span>] subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 输出3，拿到最后发出的内容3</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br><span class="line">[subject sendNext:@<span class="number">1</span>];</span><br><span class="line">[subject sendNext:@<span class="number">2</span>];</span><br><span class="line">[subject sendNext:@<span class="number">3</span>];</span><br></pre></td></tr></table></figure><h3 id="deliverOn"><a href="#deliverOn" class="headerlink" title="deliverOn"></a>deliverOn</h3><p>内容传递切换到指定线程中，副作用在原来线程中，把在创建信号时 block 中的代码称之为副作用。</p><h3 id="subscribeOn"><a href="#subscribeOn" class="headerlink" title="subscribeOn"></a>subscribeOn</h3><p>内容传递和副作用都会切换到指定线程中。</p><h2 id="常用类"><a href="#常用类" class="headerlink" title="常用类"></a>常用类</h2><h3 id="RACSubject"><a href="#RACSubject" class="headerlink" title="RACSubject"></a>RACSubject</h3><p>信号提供者，本身可以充当信号，又能发送信号，继承自 RACSignal，但是底层实现跟 RACSignal 有些不一样，当订阅信号的时候会创建订阅者并保存订阅响应 Block，而发送信号的时候会遍历订阅者，然后分别调用 nextBlock。它提供的 API 很少，但是经常使用，因为它继承自 RACSignal。这里顺便来看一下方法 flatten 跟 switchToLatest，这两个都只能用来处理信号中的信号。</p><h4 id="flatten"><a href="#flatten" class="headerlink" title="flatten"></a>flatten</h4><p>压平信号中的信号，信号中的信号我们称之为子信号，flatten 可以拿到所有子信号发送的值。</p><figure class="highlight objc"><figcaption><span>flatten</span></figcaption><table><tr><td class="code"><pre><span class="line">RACSubject *subject = [RACSubject subject];</span><br><span class="line">RACSubject *subSubject1 = [RACSubject subject];</span><br><span class="line">RACSubject *subSubject2 = [RACSubject subject];</span><br><span class="line">[subject subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 分别输出subSubject1，subSubject2，但是不能拿到其中的值</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br><span class="line">[subject.flatten subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 分别输出1, 2，flatten可以拿到所有子信号发送的值</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br><span class="line">[subject sendNext:subSubject1];</span><br><span class="line">[subject sendNext:subSubject2];</span><br><span class="line">[subSubject1 sendNext:@<span class="number">1</span>];</span><br><span class="line">[subSubject2 sendNext:@<span class="number">2</span>];</span><br></pre></td></tr></table></figure><h4 id="switchToLatest"><a href="#switchToLatest" class="headerlink" title="switchToLatest"></a>switchToLatest</h4><p>与 flatten 相同，压平信号中的信号，不同的是，在存在多个子信号时候只会拿到最新的子信号，然后输出最新的子信号的值。</p><figure class="highlight objc"><figcaption><span>switchToLatest</span></figcaption><table><tr><td class="code"><pre><span class="line">RACSubject *subject = [RACSubject subject];</span><br><span class="line">RACSubject *subSubject1 = [RACSubject subject];</span><br><span class="line">RACSubject *subSubject2 = [RACSubject subject];</span><br><span class="line">[subject subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 分别输出subSubject1，subSubject2，但是不能拿到其中的值</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br><span class="line">[subject.switchToLatest subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 输出2，switchToLatest只会拿到最新的子信号发送的值</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br><span class="line">[subject sendNext:subSubject1];</span><br><span class="line">[subject sendNext:subSubject2];</span><br><span class="line">[subSubject1 sendNext:@<span class="number">1</span>];</span><br><span class="line">[subSubject2 sendNext:@<span class="number">2</span>];</span><br></pre></td></tr></table></figure><h3 id="RACReplaySubject"><a href="#RACReplaySubject" class="headerlink" title="RACReplaySubject"></a>RACReplaySubject</h3><p>重复提供信号类，继承自 RACSubject，它可以先发送信号，再订阅信号，原理就是将发送的信号内容保存了起来，当订阅信号的时候再将之前保存的信号，由订阅者一个一个的发送出来，而保存信号的容量由 capacity 来控制。</p><figure class="highlight objc"><figcaption><span>RACReplaySubject</span></figcaption><table><tr><td class="code"><pre><span class="line">RACReplaySubject *replaySubject = [RACReplaySubject replaySubjectWithCapacity:<span class="number">5</span>];</span><br><span class="line">[replaySubject subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 输出1</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br><span class="line">[replaySubject sendNext:@<span class="number">1</span>];</span><br><span class="line">[replaySubject subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 输出1</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><h3 id="RACMulticastConnection"><a href="#RACMulticastConnection" class="headerlink" title="RACMulticastConnection"></a>RACMulticastConnection</h3><p>这是一个组播连接类，是对信号的一个封装处理，当一个信号被多次订阅时，则会多次执行 didSubscribe 这个 Block，造成副作用，而这个类就能避免多次执行 didSubscribe，是一对多的单向数据流，一般用来处理信号被多次订阅的情况。</p><figure class="highlight objc"><figcaption><span>RACMulticastConnection</span></figcaption><table><tr><td class="code"><pre><span class="line">__block <span class="keyword">int</span> i = <span class="number">0</span>;</span><br><span class="line"><span class="comment">// 创建信号</span></span><br><span class="line">RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span>&lt;RACSubscriber&gt;  _Nonnull subscriber) &#123;</span><br><span class="line">    [subscriber sendNext:@(i)];</span><br><span class="line">    i ++;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;];</span><br><span class="line"><span class="comment">//创建RACMulticastConnection对象</span></span><br><span class="line">RACMulticastConnection *connect = [signal publish]</span><br><span class="line"> [connect.signal subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 输出0</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br><span class="line">[connect.signal subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 输出0，当再次订阅时，不会再执行didSubscribe，所以并没有i++</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br><span class="line"><span class="comment">// 连接</span></span><br><span class="line">[connect connect];</span><br></pre></td></tr></table></figure><p>方法解析及实现原理：</p><ul><li>publish，multicast：这是对RACMulticastConnection初始化方法的一个封装，publish其实就是调用了multicast，并把创建好的RACSubject对象传给它，而multicast也就是调用了RACMulticastConnection的初始化方法，将原始信号传给source，把RACSubject对象传给subject。</li><li>当我们订阅connect.signal，其实就是订阅subject，然后将subject的订阅者保存起来，而调用[connect connect]的时候，会订阅原始信号（source），而source的订阅者就是subject，这时候subject就会执行[subject sendNext]，之后就会遍历subject所有的订阅者，逐一发送信号，触发外部subscribeNext回调。</li></ul><h3 id="RACCommand"><a href="#RACCommand" class="headerlink" title="RACCommand"></a>RACCommand</h3><p>这是一个命令类，可以把事件如何处理，事件中的数据如何传递，包装到这个类中，他可以很方便的监控事件的执行过程，一般来说是在UI上的某些动作来触发这些事件，比如点击一个按钮，RACCommand的实例能够决定是否可以被执行，一般用于网络请求，监控请求过程。</p><ol><li>创建命令，初始化RACCommand对象</li><li>在signalBlock中创建RACSignal，用来做数据传递，如果不需要可以创建空信号[RACSignal empty]。</li><li>执行命令execute</li></ol><figure class="highlight objc"><figcaption><span>RACCommand</span></figcaption><table><tr><td class="code"><pre><span class="line"><span class="comment">// 1. 创建命令对象</span></span><br><span class="line">RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(<span class="keyword">id</span> input) &#123;</span><br><span class="line">    <span class="comment">// 输出1，由execute传入</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, input);</span><br><span class="line">    <span class="comment">// 2. 创建信号</span></span><br><span class="line">    <span class="keyword">return</span> [RACSignal createSignal:^RACDisposable *(<span class="keyword">id</span>&lt;RACSubscriber&gt; subscriber) &#123;</span><br><span class="line">        [subscriber sendNext:@(<span class="number">2</span>)];</span><br><span class="line">        <span class="comment">// 注意：数据传递完，最好调用sendCompleted，这时命令才执行完毕。</span></span><br><span class="line">        [subscriber sendCompleted];</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">    &#125;];</span><br><span class="line">&#125;];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取信号传输的数据</span></span><br><span class="line">[command.executionSignals.switchToLatest subscribeNext:^(<span class="keyword">id</span> x) &#123;</span><br><span class="line">    <span class="comment">// 输出2</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br><span class="line"><span class="comment">// 这里用flatten跟switchToLatest也是一样的</span></span><br><span class="line">[[command.executionSignals flatten] subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 输出2</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 监听命令是否执行完毕,初始化时会调用一次，用skip直接跳过。</span></span><br><span class="line">[[command.executing skip:<span class="number">1</span>] subscribeNext:^(<span class="keyword">id</span> x) &#123;</span><br><span class="line">    <span class="keyword">if</span> ([x boolValue] == <span class="literal">YES</span>) &#123;</span><br><span class="line">        <span class="comment">// 正在执行</span></span><br><span class="line">        <span class="built_in">NSLog</span>(<span class="string">@"正在执行"</span>);</span><br><span class="line">    &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">        <span class="comment">// 执行完成</span></span><br><span class="line">        <span class="built_in">NSLog</span>(<span class="string">@"执行完成"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 执行命令</span></span><br><span class="line">RACSignal *connectSignal = [command execute:@<span class="number">1</span>] ;</span><br><span class="line">[connectSignal subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 输出2，connectSignal是connect.signal</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><h3 id="RACChannel"><a href="#RACChannel" class="headerlink" title="RACChannel"></a>RACChannel</h3><p>这是一个通道类，可以理解为一个双向的连接，连接的两端都配有RACChannelTerminal（通道终端，继承自RACSignal，且又实现了RACSubscriber协议，所以它可以充当信号，又能发送信号），分别是leadingTerminal，followingTerminal，只要其中任何一端输出信号，另一端都会有相同的信号输出。我们平时很少直接使用RACChannel，而是使用RACChannelTo。</p><figure class="highlight objc"><figcaption><span>RACChannel</span></figcaption><table><tr><td class="code"><pre><span class="line">RACChannelTerminal *followT = RACChannelTo(view, backgroundColor);</span><br><span class="line">[followT subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 每点击一次就输出一次随机颜色</span></span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br><span class="line"><span class="built_in">UITapGestureRecognizer</span> *tap = [[<span class="built_in">UITapGestureRecognizer</span> alloc]init];</span><br><span class="line">[tap.rac_gestureSignal subscribeNext:^(__kindof <span class="built_in">UIGestureRecognizer</span> * _Nullable x) &#123;</span><br><span class="line">    <span class="comment">// 改变view.backgroundColor</span></span><br><span class="line">    [followT sendNext:RandomColor];</span><br><span class="line">&#125;];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 将_textField.backgroundColor跟view.backgroundColor绑定</span></span><br><span class="line">RACChannelTo(button, backgroundColor) = RACChannelTo(view, backgroundColor);</span><br></pre></td></tr></table></figure><h2 id="常用宏"><a href="#常用宏" class="headerlink" title="常用宏"></a>常用宏</h2><h3 id="RAC-TARGET"><a href="#RAC-TARGET" class="headerlink" title="RAC(TARGET, ...)"></a>RAC(TARGET, ...)</h3><p>给某个对象的某个属性进行绑定。</p><figure class="highlight objc"><figcaption><span>RAC(TARGET, ...)</span></figcaption><table><tr><td class="code"><pre><span class="line"><span class="comment">// 当textfield开始编辑时，关闭button响应</span></span><br><span class="line">RAC(_button, enabled) = [[<span class="keyword">self</span> rac_signalForSelector:<span class="keyword">@selector</span>(textFieldDidBeginEditing:) fromProtocol:<span class="class"><span class="keyword">@protocol</span>(<span class="title">UITextFieldDelegate</span>)] <span class="title">mapReplace</span>:@<span class="title">NO</span>];</span></span><br></pre></td></tr></table></figure><h3 id="RACObserve-TARGET-KEYPATH"><a href="#RACObserve-TARGET-KEYPATH" class="headerlink" title="RACObserve(TARGET, KEYPATH)"></a>RACObserve(TARGET, KEYPATH)</h3><p>KVO，监听某个对象的属性，返回的是信号。</p><figure class="highlight objc"><figcaption><span>RACObserve(TARGET, KEYPATH)</span></figcaption><table><tr><td class="code"><pre><span class="line">[RACObserve(<span class="keyword">self</span>.view, backgroundColor) subscribeNext:^(<span class="keyword">id</span>  _Nullable x) &#123;</span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, x);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><h3 id="RACChannelTo"><a href="#RACChannelTo" class="headerlink" title="RACChannelTo"></a>RACChannelTo</h3><p>用于双向绑定的一个通道终端。</p><figure class="highlight objc"><figcaption><span>RACChannelTo</span></figcaption><table><tr><td class="code"><pre><span class="line"><span class="comment">// 将_textField.backgroundColor跟view.backgroundColor绑定</span></span><br><span class="line">RACChannelTo(button, backgroundColor) = RACChannelTo(view, backgroundColor);</span><br></pre></td></tr></table></figure><h3 id="RACTuplePack"><a href="#RACTuplePack" class="headerlink" title="RACTuplePack"></a>RACTuplePack</h3><p>将数据包装成 RACTuple（元组）。</p><figure class="highlight objc"><figcaption><span>RACTuplePack</span></figcaption><table><tr><td class="code"><pre><span class="line">RACTuple *tuple = RACTuplePack(@<span class="number">1</span>, @<span class="number">2</span>, <span class="string">@"3"</span>);</span><br></pre></td></tr></table></figure><h3 id="RACTupleUnpack"><a href="#RACTupleUnpack" class="headerlink" title="RACTupleUnpack(...)"></a>RACTupleUnpack(...)</h3><p>把元组解包成对应的数据</p><figure class="highlight objc"><figcaption><span>RACTupleUnpack(...)</span></figcaption><table><tr><td class="code"><pre><span class="line"><span class="comment">// 传入需要解析生成的变量名，从第一个开始解析</span></span><br><span class="line">RACTupleUnpack(<span class="built_in">NSNumber</span> *num1, <span class="built_in">NSNumber</span> *num2) = tuple;</span><br><span class="line"><span class="comment">// 输出 1, 2</span></span><br><span class="line"><span class="built_in">NSLog</span>(<span class="string">@"%@, %@"</span>, num1, num2);</span><br></pre></td></tr></table></figure><div class="article_footer"><section class="widget list references desktop mobile"><header><i class="fas fa-quote-left fa-fw" aria-hidden="true"></i><span class="name">参考资料</span></header><div class="content"><ul class="list entry navigation"><li><a class="flat-box" title="https://www.jianshu.com/p/8dbe8878d1c5" href="https://www.jianshu.com/p/8dbe8878d1c5" id="https:wwwjianshucomp8dbe8878d1c5" rel="external nofollow noopener noreferrer" target="_blank"><div class="name">iOS RAC学习之路（二）</div></a></li></ul></div></section><section class="widget related_posts desktop mobile"><header><i class="fas fa-bookmark fa-fw" aria-hidden="true"></i><span class="name">相关文章</span></header><div class="content"><ul class="popular-posts"><li class="popular-posts-item"><div class="popular-posts-title"><h3><a href="/blog/2016-06-22-ios-ble/" title="浅谈 iOS 蓝牙 BLE 开发" rel="bookmark">浅谈 iOS 蓝牙 BLE 开发</a></h3></div></li><li class="popular-posts-item"><div class="popular-posts-title"><h3><a href="/blog/2016-09-12-sandbox/" title="iOS 链式封装，更优雅地存取沙盒文件" rel="bookmark">iOS 链式封装，更优雅地存取沙盒文件</a></h3></div></li><li class="popular-posts-item"><div class="popular-posts-title"><h3><a href="/blog/2016-09-14-get-color/" title="从 UIImageView 指定点取色" rel="bookmark">从 UIImageView 指定点取色</a></h3></div></li><li class="popular-posts-item"><div class="popular-posts-title"><h3><a href="/blog/2016-11-06-ios-framework/" title="如何封装 Framework 静态库" rel="bookmark">如何封装 Framework 静态库</a></h3></div></li><li class="popular-posts-item"><div class="popular-posts-title"><h3><a href="/blog/2016-03-21-cocoapods/" title="macOS10.12 最新版 CocoaPods 的安装及使用" rel="bookmark">macOS10.12 最新版 CocoaPods 的安装及使用</a></h3></div></li></ul></div></section><section class="widget copyright desktop mobile"><div class="content"><blockquote><p>博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议</p><p>本文永久链接是：<a href="https://xaoxuu.com/blog/2016-07-20-reactive-cocoa/">https://xaoxuu.com/blog/2016-07-20-reactive-cocoa/</a></p></blockquote></div></section></div></div><section class="meta" id="footer-meta"><div class="new-meta-box"><div class="new-meta-item author"><a href="https://xaoxuu.com" rel="nofollow"><img no-lazy="" src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/avatar/avatar.png"><p>Mr. X</p></a></div><div class="new-meta-item date" itemprop="dateUpdated" datetime="2018-09-27T00:00:00+08:00"><a class="notlink"><i class="fas fa-edit fa-fw" aria-hidden="true"></i><p>edited on 2018-09-27</p></a></div><div class="new-meta-item share -mob-share-list"><div class="-mob-share-list share-body"><a class="-mob-share-qq" rel="external nofollow noopener noreferrer" href="http://connect.qq.com/widget/shareqq/index.html?url=https://xaoxuu.com/blog/2016-07-20-reactive-cocoa/&title=ReactiveCocoa 响应式编程初探 - xaoxuu&summary=RAC 即 ReactiveCocoa，是 Github 开源的函数响应式编程框架。可以简化代码，使一个功能的代码集中到一起，提高了代码聚合性。" target="_blank"><img src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/placeholder/c617bfd2497fcea598e621413e315c368f8d8e.svg" data-original="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/logo/128/qq.png"> </a><a class="-mob-share-qzone" rel="external nofollow noopener noreferrer" href="https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=https://xaoxuu.com/blog/2016-07-20-reactive-cocoa/&title=ReactiveCocoa 响应式编程初探 - xaoxuu&summary=RAC 即 ReactiveCocoa，是 Github 开源的函数响应式编程框架。可以简化代码，使一个功能的代码集中到一起，提高了代码聚合性。" target="_blank"><img src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/placeholder/c617bfd2497fcea598e621413e315c368f8d8e.svg" data-original="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/logo/128/qzone.png"> </a><a class="-mob-share-weibo" rel="external nofollow noopener noreferrer" href="http://service.weibo.com/share/share.php?url=https://xaoxuu.com/blog/2016-07-20-reactive-cocoa/&title=ReactiveCocoa 响应式编程初探 - xaoxuu&summary=RAC 即 ReactiveCocoa，是 Github 开源的函数响应式编程框架。可以简化代码，使一个功能的代码集中到一起，提高了代码聚合性。" target="_blank"><img src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/placeholder/c617bfd2497fcea598e621413e315c368f8d8e.svg" data-original="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/logo/128/weibo.png"></a><div class="hoverbox"><a><img src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/placeholder/c617bfd2497fcea598e621413e315c368f8d8e.svg" data-original="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/logo/128/wechat.png"></a><div class="target"><img src=""></div></div></div></div></div></section><div class="prev-next"><a class="prev" href="/blog/2016-08-11-cocoapods/"><p class="title"><i class="fas fa-chevron-left" aria-hidden="true"></i>如何发布开源库到 CocoaPods</p><p class="content">说实在话，类似的教程网上很多，基本可以满足大多数人零基础发布 CocoaPods，但是其中可能会遇到一些问题，只有亲自尝试过才知道。准备工作1. clone 远程仓库到本地git clone 你...</p></a><a class="next" href="/blog/2016-06-22-ios-ble/"><p class="title">浅谈 iOS 蓝牙 BLE 开发<i class="fas fa-chevron-right" aria-hidden="true"></i></p><p class="content">BLE，即 Bluetooth Low Energy 低功耗蓝牙。可穿戴产品、无线耳机、无线鼠标等低功耗外设都使用 BLE 技术。轩霆科技所使用的手环和手表产品就是通过 BLE 连接与手机同步数...</p></a></div></section></article><article class="post white-box reveal comments shadow"><section class="article typo"><p ct=""><i class="fas fa-comments"></i> 评论</p><section id="comments"><div id="valine_container" class="valine_thread"><i class="fas fa-cog fa-spin fa-fw fa-2x"></i></div></section></section></article></div><aside class="l_side"><section class="widget tagcloud shadow desktop mobile"><header><a href="/blog/tags/"><i class="fas fa-tags fa-fw" aria-hidden="true"></i><span class="name">热门标签</span></a></header><div class="content"><a href="/blog/tags/alamofire/" style="font-size:14px;color:#999">Alamofire</a> <a href="/blog/tags/ble/" style="font-size:14px;color:#999">BLE</a> <a href="/blog/tags/cocoapods/" style="font-size:16px;color:#8b8b8b">CocoaPods</a> <a href="/blog/tags/framework/" style="font-size:14px;color:#999">Framework</a> <a href="/blog/tags/gcd/" style="font-size:14px;color:#999">GCD</a> <a href="/blog/tags/objc/" style="font-size:18px;color:#7e7e7e">ObjC</a> <a href="/blog/tags/reactivecocoa/" style="font-size:14px;color:#999">ReactiveCocoa</a> <a href="/blog/tags/runtime/" style="font-size:16px;color:#8b8b8b">Runtime</a> <a href="/blog/tags/socket/" style="font-size:14px;color:#999">Socket</a> <a href="/blog/tags/swift/" style="font-size:20px;color:#707070">Swift</a> <a href="/blog/tags/appledoc/" style="font-size:14px;color:#999">appledoc</a> <a href="/blog/tags/ios/" style="font-size:24px;color:#555">iOS</a> <a href="/blog/tags/%E5%93%8D%E5%BA%94%E5%BC%8F%E7%BC%96%E7%A8%8B/" style="font-size:14px;color:#999">响应式编程</a> <a href="/blog/tags/%E5%BC%80%E6%BA%90%E5%BA%93/" style="font-size:22px;color:#636363">开源库</a> <a href="/blog/tags/%E6%9E%B6%E6%9E%84/" style="font-size:14px;color:#999">架构</a> <a href="/blog/tags/%E6%B2%99%E7%9B%92/" style="font-size:14px;color:#999">沙盒</a> <a href="/blog/tags/%E7%9B%B4%E6%92%AD/" style="font-size:14px;color:#999">直播</a> <a href="/blog/tags/%E7%BC%93%E5%AD%98/" style="font-size:14px;color:#999">缓存</a> <a href="/blog/tags/%E8%A7%A3%E8%80%A6/" style="font-size:14px;color:#999">解耦</a> <a href="/blog/tags/%E9%9D%99%E6%80%81%E5%BA%93/" style="font-size:14px;color:#999">静态库</a> <a href="/blog/tags/%E9%A1%B9%E7%9B%AE%E6%A1%86%E6%9E%B6/" style="font-size:14px;color:#999">项目框架</a></div></section><section class="widget toc-wrapper shadow desktop mobile" id="toc-div"><header><i class="fas fa-list fa-fw" aria-hidden="true"></i><span class="name">本文目录</span></header><div class="content"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#应用场景"><span class="toc-text">应用场景</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#常用方法"><span class="toc-text">常用方法</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#map-amp-flattenMap"><span class="toc-text">map &amp; flattenMap</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#merge"><span class="toc-text">merge</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#concat"><span class="toc-text">concat</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#then"><span class="toc-text">then</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#zip"><span class="toc-text">zip</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#combineLatest"><span class="toc-text">combineLatest</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#rac-liftSelector-withSignalsFromArray"><span class="toc-text">rac_liftSelector: withSignalsFromArray:</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#reduceEach"><span class="toc-text">reduceEach</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#filter"><span class="toc-text">filter</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#distinctUntilChanged"><span class="toc-text">distinctUntilChanged</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#take"><span class="toc-text">take</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#takeLast"><span class="toc-text">takeLast</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#takeUntil"><span class="toc-text">takeUntil</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#skip"><span class="toc-text">skip</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#doNext"><span class="toc-text">doNext</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#timeout"><span class="toc-text">timeout</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#interval"><span class="toc-text">interval</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#delay"><span class="toc-text">delay</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#retry"><span class="toc-text">retry</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#throttle"><span class="toc-text">throttle</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#deliverOn"><span class="toc-text">deliverOn</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#subscribeOn"><span class="toc-text">subscribeOn</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#常用类"><span class="toc-text">常用类</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#RACSubject"><span class="toc-text">RACSubject</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#flatten"><span class="toc-text">flatten</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#switchToLatest"><span class="toc-text">switchToLatest</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RACReplaySubject"><span class="toc-text">RACReplaySubject</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RACMulticastConnection"><span class="toc-text">RACMulticastConnection</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RACCommand"><span class="toc-text">RACCommand</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RACChannel"><span class="toc-text">RACChannel</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#常用宏"><span class="toc-text">常用宏</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#RAC-TARGET"><span class="toc-text">RAC(TARGET, ...)</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RACObserve-TARGET-KEYPATH"><span class="toc-text">RACObserve(TARGET, KEYPATH)</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RACChannelTo"><span class="toc-text">RACChannelTo</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RACTuplePack"><span class="toc-text">RACTuplePack</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RACTupleUnpack"><span class="toc-text">RACTupleUnpack(...)</span></a></li></ol></li></ol></div></section></aside></div><footer class="clearfix"><br><br><br><div class="social-wrapper"><a href="/atom.xml" class="social fas fa-rss flat-btn" target="_blank" rel="external nofollow noopener noreferrer"></a> <a href="mailto:me@xaoxuu.com" class="social fas fa-envelope flat-btn" target="_blank" rel="external nofollow noopener noreferrer"></a> <a href="https://github.com/xaoxuu" class="social fab fa-github flat-btn" target="_blank" rel="external nofollow noopener noreferrer"></a> <a href="https://music.163.com/#/user/home?id=63035382" class="social fas fa-headphones-alt flat-btn" target="_blank" rel="external nofollow noopener noreferrer"></a></div>本站使用 <a href="https://volantis.js.org/" target="_blank" class="codename" rel="external nofollow noopener noreferrer">Volantis</a> 作为主题，总访问量为 <span id="busuanzi_value_site_pv"><i class="fas fa-circle-notch fa-spin fa-fw" aria-hidden="true"></i></span> 次<div class="copyright"><p><a href="https://xaoxuu.com">Copyright © 2017-2020 Mr. X</a></p></div></footer><a class="s-top fas fa-arrow-up fa-fw" href="javascript:void(0)"></a></div><div><script src="https://cdn.jsdelivr.net/npm/jquery@3.4/dist/jquery.min.js"></script><script>var SEARCH_SERVICE="hexo",ROOT="/";ROOT.endsWith("/")||(ROOT+="/")</script><script async src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-volantis@2/js/instant_page.js" type="module" defer integrity="sha384-OeDn4XE77tdHo8pGtE1apMPmAipjoxUQ++eeJa6EtJCfHlvijigWiJpD7VDPWXV1"></script><script src="https://cdn.jsdelivr.net/npm/jquery-backstretch@2.1.18/jquery.backstretch.min.js"></script><script type="text/javascript">$(function(){var a=["https://cdn.jsdelivr.net/gh/xaoxuu/cdn-wallpaper/abstract/41F215B9-261F-48B4-80B5-4E86E165259E.jpeg"];!function(a){for(var t=a.length;t--;){var r=Math.floor(Math.random()*t),n=a[r];a[r]=a[t],a[t]=n}}(a),$(".cover").backstretch(a,{duration:"20000",fade:"1500"})})</script><script src="/js/valine.js"></script><script>var GUEST_INFO=["nick","mail","link"],meta="nick,mail,link".split(",").filter(function(i){return-1<GUEST_INFO.indexOf(i)}),REQUIRED_FIELDS=["nick","mail","link"],requiredFields="nick,mail".split(",").filter(function(i){return-1<REQUIRED_FIELDS.indexOf(i)});function emoji(i,e,a){return i+"/"+i+"-"+e+"."+a}for(var emojiMaps={},i=1;i<=54;i++)emojiMaps["tieba-"+i]=emoji("tieba",i,"png");for(i=1;i<=101;i++)emojiMaps["qq-"+i]=emoji("qq",i,"gif");for(i=1;i<=116;i++)emojiMaps["aru-"+i]=emoji("aru",i,"gif");for(i=1;i<=125;i++)emojiMaps["twemoji-"+i]=emoji("twemoji",i,"png");for(i=1;i<=4;i++)emojiMaps["weibo-"+i]=emoji("weibo",i,"png");function pjax_valine(){var i="none"===$.trim($("#valine-path").text())?window.location.pathname:$.trim($("#valine-path").text()),e="none"===$.trim($("#valine-placeholder").text())?"快来评论吧~":$.trim($("#valine-placeholder").text());(new Valine).init({el:"#valine_container",meta:meta,placeholder:e,path:i,appId:"E8AmXTJ4Q2qpUlf4S9py8dkf-MdYXbMMI",appKey:"MPMrJ0lgp3JQQQfYaCveJm3J",pageSize:"10",avatar:"robohash",lang:"zh-cn",visitor:"true",highlight:"true",mathJax:"false",enableQQ:"true",requiredFields:requiredFields,emojiCDN:"https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/emoji/valine/",emojiMaps:emojiMaps})}$(function(){pjax_valine()})</script><script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script><script>function wait(callback, seconds) {
        var timelag = null;
        timelag = window.setTimeout(callback, seconds)
    }

    function pjax_initCopyCode() {
        var copyHtml = '';
        copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
        copyHtml += '<i class="fas fa-copy"></i><span>COPY</span>';
        copyHtml += '</button>';
        $(".highlight .code pre").before(copyHtml);
        $(".article pre code").before(copyHtml);
        var clipboard = new ClipboardJS('.btn-copy', {
            target: function (trigger) {
                return trigger.nextElementSibling
            }
        });
        clipboard.on('success', function (e) {
            let $btn = $(e.trigger);
            $btn.addClass('copied');
            let $icon = $($btn.find('i'));
            $icon.removeClass('fa-copy');
            $icon.addClass('fa-check-circle');
            let $span = $($btn.find('span'));
            $span[0].innerText = 'COPIED';
            wait(function () {
                $icon.removeClass('fa-check-circle');
                $icon.addClass('fa-copy');
                $span[0].innerText = 'COPY'
            }, 2000)
        });
        clipboard.on('error', function (e) {
            e.clearSelection();
            let $btn = $(e.trigger);
            $btn.addClass('copy-failed');
            let $icon = $($btn.find('i'));
            $icon.removeClass('fa-copy');
            $icon.addClass('fa-times-circle');
            let $span = $($btn.find('span'));
            $span[0].innerText = 'COPY FAILED';
            wait(function () {
                $icon.removeClass('fa-times-circle');
                $icon.addClass('fa-copy');
                $span[0].innerText = 'COPY'
            }, 2000)
        })
    }
    $(function () {
        pjax_initCopyCode()
    });</script><script src="https://cdn.jsdelivr.net/npm/scrollreveal@4.0.6/dist/scrollreveal.min.js"></script><script type="text/javascript">function pjax_scrollrebeal(){ScrollReveal().reveal(".l_main .reveal",{distance:"32px",duration:"800",interval:"20",scale:"1",easing:"ease-out"})}$(function(){pjax_scrollrebeal()})</script><script src="https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js"></script><script>function pjax_fancybox(){$(".article-entry").find("img").not(".inline").not("a img").each(function(){var t=document.createElement("a");$(t).attr("pjax-fancybox",""),$(t).attr("href",$(this).attr("src")),$(this).attr("data-original")&&$(t).attr("href",$(this).attr("data-original")),$(t).attr("data-fancybox","images");var a="";$(this).attr("alt")&&($(t).attr("data-caption",$(this).attr("alt")),a=$(this).attr("alt"));var e=document.createElement("div");$(e).addClass("fancybox"),$(this).wrap(e);var i=document.createElement("span");$(i).addClass("image-caption"),$(i).text(a),$(this).after(i),$(this).wrap(t)}),$(".article-entry").find("img").fancybox({selector:'[data-fancybox="images"]',hash:!1,loop:!1,closeClick:!0,helpers:{overlay:{closeClick:!0}},buttons:["zoom","close"]})}$(function(){pjax_fancybox()})</script><script src="/js/app.js"></script><script src="/js/search.js"></script><script defer src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-busuanzi@2.3/js/busuanzi.pure.mini.js" data-pjax=""></script><script src="https://cdn.jsdelivr.net/npm/node-waves@0.7.6/dist/waves.min.js"></script><script type="text/javascript">$(function(){Waves.attach(".flat-btn",["waves-button"]),Waves.attach(".float-btn",["waves-button","waves-float"]),Waves.attach(".float-btn-light",["waves-button","waves-float","waves-light"]),Waves.attach(".flat-box",["waves-block"]),Waves.attach(".float-box",["waves-block","waves-float"]),Waves.attach(".waves-image"),Waves.init()})</script><script src="https://cdn.jsdelivr.net/npm/pjax@0.2.8/pjax.min.js"></script><div class="pjax-animate"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.css"><script src="https://cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.min.js"></script><div id="loading-bar-wrapper"><script>NProgress.configure({parent:"#loading-bar-wrapper",trickleSpeed:100})</script></div><script>window.ShowLoading=function(){NProgress.start()},window.HideLoading=function(){NProgress.done()}</script></div><script>var pjax;
    document.addEventListener('DOMContentLoaded', function () {
      pjax = new Pjax({
        elements: 'a[href]:not([href^="#"]):not([href="javascript:void(0)"]):not([pjax-fancybox])',
        selectors: [
          "title",
          "#pjax-container"
        ],
        cacheBust: false,   // url 地址追加时间戳，用以避免浏览器缓存
        timeout: 5000
      });
    });
  
    document.addEventListener('pjax:send', function (e) {
      window.stop(); // 相当于点击了浏览器的停止按钮
      var currentUrl = window.location.pathname;
      var targetUrl = e.triggerElement.href;
      var banUrl = [""];
      if (banUrl[0] != "") {
        banUrl.forEach(item => {
          if(currentUrl.indexOf(item) != -1 || targetUrl.indexOf(item) != -1) {
            window.location.href = targetUrl;
          }
        });
      }
      window.subData = null; // 移除标题（用于一二级导航栏切换处）
      $.fancybox.close();    // 关闭弹窗
      $('.nav-main').find('.list-v').not('.menu-phone').css("display","none"); // 移除小尾巴
      $('.menu-phone.list-v').css("display","none"); // 移除小尾巴
      $('.l_header .switcher .s-search').removeClass('active'); // 关闭移动端激活的搜索框
      $('.l_header').removeClass('z_search-open'); // 关闭移动端激活的搜索框
      $('.wrapper').removeClass('sub'); // 跳转页面时关闭二级导航
      $(window).unbind('resize');    // 解绑
      $(window).unbind('scroll');    // 解绑
      $(window).unbind('click');     // 解绑
      $(document).unbind('scroll');  // 解绑
      window.ShowLoading();
    });
  
    document.addEventListener('pjax:complete', function () {
      // 关于百度统计对 SPA 页面的处理：
      // 方案一：百度统计>管理>单页应用设置中，打开开启按钮即可对SPA进行统计。 https://tongji.baidu.com/web/help/article?id=324
      // 方案二：取消注释下列代码。 https://tongji.baidu.com/web/help/article?id=235
      // 
  
      // 关于谷歌统计对 SPA 页面的处理：
      // 当应用以动态方式加载内容并更新地址栏中的网址时，也应该更新通过 gtag.js 存储的网页网址。
      // https://developers.google.cn/analytics/devguides/collection/gtagjs/single-page-applications?hl=zh-cn
      
      
      $('.nav-main').find('.list-v').not('.menu-phone').removeAttr("style",""); // 移除小尾巴的移除
      $('.menu-phone.list-v').removeAttr("style",""); // 移除小尾巴的移除
      $('script[data-pjax], .pjax-reload script').each(function () {
        $(this).parent().append($(this).remove());
      });
  
      try{
        pjax_fancybox();
        
          
          if ('.cover') {
            $('.cover').backstretch("resize");
          } else {
            $.backstretch("resize");
          }
        
        
          pjax_scrollrebeal();
        
        
          pjax_initCopyCode();
        
        
          pjax_valine();
        
        
        
      } catch (e) {
        console.log(e);
      }
      window.HideLoading();
    });
  
    document.addEventListener('pjax:error', function (e) { 
      window.HideLoading(); 
      window.location.href = e.triggerElement.href; 
    });</script></div><script>
            window.imageLazyLoadSetting = {
                isSPA: true,
                processImages: null,
            };
        </script><script>window.addEventListener("load",function(){var t=/\.(gif|jpg|jpeg|tiff|png)$/i,r=/^data:image\/[a-z]+;base64,/;Array.prototype.slice.call(document.querySelectorAll("img[data-original]")).forEach(function(a){var e=a.parentNode;"A"===e.tagName&&(e.href.match(t)||e.href.match(r))&&(e.href=a.dataset.original)})});</script><script>!function(n){n.imageLazyLoadSetting.processImages=o;var i=n.imageLazyLoadSetting.isSPA,r=Array.prototype.slice.call(document.querySelectorAll("img[data-original]"));function o(){i&&(r=Array.prototype.slice.call(document.querySelectorAll("img[data-original]")));for(var t,e,a=0;a<r.length;a++)t=r[a],e=void 0,0<=(e=t.getBoundingClientRect()).bottom&&0<=e.left&&e.top<=(n.innerHeight||document.documentElement.clientHeight)&&function(){var t,e,n,i,o=r[a];t=o,e=function(){r=r.filter(function(t){return o!==t})},n=new Image,i=t.getAttribute("data-original"),n.onload=function(){t.src=i,e&&e()},n.src=i}()}o(),n.addEventListener("scroll",function(){var t,e;t=o,e=n,clearTimeout(t.tId),t.tId=setTimeout(function(){t.call(e)},500)})}(this);</script></body></html>